package gui.tabs;

import gui.ProcessorPanel;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;

import misc.TextPrompt;
import misc.TextPrompt.Show;

import data.ActionList;
import data.ProcList;
import data.model.Action;
import data.model.ProcAction;
import data.model.Processor;
import data.model.TaskGraph;

public class ProcessorTab extends AbstractTab implements ActionListener {

	private JTextArea actDisp;
	
	private JTextField texCostA;
	
	private JTextField texCostI;
	
	private JTextField uTimeTex;
	
	private JTextField lTimeTex;
	
	private JTextField pNumTex;
	
	private JTextField pDenomTex;
	
	private JComboBox comboAct;

	private JButton addActBtn;

	private JButton editActBtn;

	private JButton clearActBtn;

	private JButton savActBtn;
	
	private boolean isActAdd;
	
	private boolean isActEdit;
	
	public ProcessorTab() {
		super();
		addComboBoxAct();
		addButtonPane("Processor");
		addInfoPane();
		addPropPane();
		actDisp = addList("Actions");
		
		combo.setSelectedIndex(0);
		comboUsed();
	}
	
	@Override
	protected void addInfoPane() {
		JPanel infoPane = new JPanel();
		infoPane.setBackground(backColour);
		
		Border raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
		
		infoPane.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		
		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0.8;
		c.weighty = 0.2;
		//c.fill = GridBagConstraints.HORIZONTAL;
		
		infoPane.add(new JLabel("Name: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = GridBagConstraints.REMAINDER;
		tex = new JTextField(10);
		tex.setEditable(false);
		tex.setBorder(raisedetched);
		tex.setBackground(uneditColour);
		
		infoPane.add(tex, c);
		
		c.gridx = 0;
		c.gridy = 1;
		c.weightx = 0.5;
		c.weighty = 0.8;
		
		infoPane.add(new JLabel("Notes: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = GridBagConstraints.REMAINDER;
		//c.weighty = GridBagConstraints.REMAINDER;
		
		texA = new JTextArea(10,10);
		texA.setLineWrap(true);
		texA.setEditable(false);
		texA.setBorder(raisedetched);
		texA.setBackground(uneditColour);
		//JScrollPane scrollPane = new JScrollPane(texA);
		
		infoPane.add(texA, c);
		
		c.gridx = 0;
		c.gridy = 4;
		c.weightx = 0.8;
		c.weighty = 0.2;
		//c.fill = GridBagConstraints.HORIZONTAL;
		
		infoPane.add(new JLabel("Active Cost: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = GridBagConstraints.REMAINDER;
		texCostA = new JTextField(10);
		texCostA.setEditable(false);
		texCostA.setBorder(raisedetched);
		texCostA.setBackground(uneditColour);
		
		infoPane.add(texCostA, c);
		
		c.gridx = 0;
		c.gridy = 5;
		c.weightx = 0.8;
		c.weighty = 0.2;
		//c.fill = GridBagConstraints.HORIZONTAL;
		
		infoPane.add(new JLabel("Inactive Cost: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = GridBagConstraints.REMAINDER;
		texCostI = new JTextField(10);
		texCostI.setEditable(false);
		texCostI.setBorder(raisedetched);
		texCostI.setBackground(uneditColour);
		
		infoPane.add(texCostI, c);
		
		this.add(infoPane);
	}
	
	@Override
	protected void addComboBox() {
		//Object[] procList = ;
		combo = new JComboBox(ProcList.getList());
		combo.setEditable(false);
		combo.setRenderer(new ProcCellRenderer());
		combo.addActionListener(this);
		
	}
	
	protected void addComboBoxAct() {
		comboAct = new JComboBox(ActionList.getList());
		comboAct.removeItemAt(0);
		comboAct.setEditable(false);
		comboAct.setRenderer(new ActCellRenderer());
		comboAct.addActionListener(this);
		comboAct.validate();
	}
	
	class ProcCellRenderer extends JLabel implements ListCellRenderer {
	     public ProcCellRenderer() {
	         setOpaque(true);
	     }

	     public Component getListCellRendererComponent(JList list,
	                                                   Object value,
	                                                   int index,
	                                                   boolean isSelected,
	                                                   boolean cellHasFocus) {
	    	 if(value != null)
	    		 setText(((Processor) value).getProcName());
	         return this;
	     }
	 }
	
	class ActCellRenderer extends JLabel implements ListCellRenderer {
	     public ActCellRenderer() {
	         setOpaque(true);
	     }
	     
	     public Component getListCellRendererComponent(JList list,
	                                                   Object value,
	                                                   int index,
	                                                   boolean isSelected,
	                                                   boolean cellHasFocus) {
	    	 if(value != null)
	    		 setText(((Action) value).getActName());
	         return this;
	     }
	 }
	
	private void addPropPane()
	{
		JPanel propPane = new JPanel();
		propPane.setBackground(backColour);
		
		Border raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);

		propPane.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		
		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0.2;
		c.weighty = 0.5;
		
		propPane.add(new JLabel("Select Action"), c); //top left
		
		c.gridx = 1;
		c.gridy = 0;
		c.weightx = 0.5;
		c.weighty = 0.5;
		//c.gridx =  GridBagConstraints.RELATIVE;
		
		propPane.add(comboAct, c); //top right	
		
		c.gridx = 0;
		c.gridy = 1;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		propPane.add(new JLabel("Time Constraints: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.2;
		lTimeTex = new JTextField(3);
		lTimeTex.setEditable(false);
		lTimeTex.setBorder(raisedetched);
		lTimeTex.setBackground(uneditColour);
		TextPrompt tp = new TextPrompt("min", lTimeTex);
		tp.setShow(Show.FOCUS_LOST);
		
		propPane.add(lTimeTex, c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.1;
		propPane.add(new JLabel("-"), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.2;
		uTimeTex = new JTextField(3);
		uTimeTex.setEditable(false);
		uTimeTex.setBorder(raisedetched);
		uTimeTex.setBackground(uneditColour);
		TextPrompt tp2 = new TextPrompt("max", uTimeTex);
		tp2.setShow(Show.FOCUS_LOST);
		
		propPane.add(uTimeTex, c);
		
		c.gridx = 0;
		c.gridy = 2;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		propPane.add(new JLabel("Probability of faults: "), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.5;
		pNumTex = new JTextField(3);
		pNumTex.setEditable(false);
		pNumTex.setBorder(raisedetched);
		pNumTex.setBackground(uneditColour);
		
		propPane.add(pNumTex, c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.1;
		propPane.add(new JLabel("/"), c);
		
		c.gridx = GridBagConstraints.RELATIVE;
		c.weightx = 0.5;
		pDenomTex = new JTextField(3);
		pDenomTex.setEditable(false);
		pDenomTex.setBorder(raisedetched);
		pDenomTex.setBackground(uneditColour);
		
		propPane.add(pDenomTex, c);
		
		c.gridx = 0;
		c.gridy = 3;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		addActBtn = new JButton("add");
		addActBtn.setEnabled(true);
		addActBtn.addActionListener(this);
		propPane.add(addActBtn, c); //mid left
		
		c.gridx = 1;
		c.gridy = 3;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		editActBtn = new JButton("edit");
		editActBtn.setEnabled(true);
		editActBtn.addActionListener(this);
		propPane.add(editActBtn, c); //mid right
		
		c.gridx = 0;
		c.gridy = 4;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		clearActBtn = new JButton("clear");
		clearActBtn.setEnabled(true);
		clearActBtn.addActionListener(this);
		propPane.add(clearActBtn, c);	//low left
		
		c.gridx = 1;
		c.gridy = 4;
		c.weightx = 0.5;
		c.weighty = 0.5;
		
		savActBtn = new JButton("save");
		savActBtn.setEnabled(true);
		savActBtn.addActionListener(this);
		propPane.add(savActBtn, c);	//low right
		
		this.add(propPane);
	}

	private void saveProcAction(int id, int idAct)
	{
		boolean change = false;
		
		if(ProcList.getProc(id).getProcAction(idAct) != null )
		{
			//prob delay over interval
			if (!lTimeTex.getText().isEmpty() && !uTimeTex.getText().isEmpty())
			{
				ProcList.getProc(id).getProcAction(idAct).SetDelay(Integer.parseInt(uTimeTex.getText()), Integer.parseInt(lTimeTex.getText()));
				change = true;
			}
			else if (!lTimeTex.getText().isEmpty())
			{
				ProcList.getProc(id).getProcAction(idAct).SetDelay(Integer.parseInt(lTimeTex.getText()), Integer.parseInt(lTimeTex.getText()));
				change = true;
			}
			else if (!uTimeTex.getText().isEmpty())
			{
				ProcList.getProc(id).getProcAction(idAct).SetDelay(Integer.parseInt(uTimeTex.getText()), Integer.parseInt(uTimeTex.getText()));
				change = true;
			}
			
			if(!pNumTex.getText().isEmpty() && !pDenomTex.getText().isEmpty())
			{
				ProcList.getProc(id).getProcAction(idAct).SetFaulty(Integer.parseInt(pNumTex.getText()), Integer.parseInt(pDenomTex.getText()));
				change = true;
			}
			
		}	
		if(!change)
			System.out.println("no change");
		
	}
	
	private boolean updateProc(int curID, boolean change) throws NumberFormatException
	{
		//update description
		if(!texA.getText().isEmpty())
		{
			ProcList.getProc(curID).setProcDesc(texA.getText());
			change = true;
		}
		//update active cost
		if(!texCostA.getText().isEmpty())
		{
			double d = Double.parseDouble(texCostA.getText());
			if(d >= 0)
			{
				ProcList.getProc(curID).setActiveCost(d);
				change = true;
			}
			else
				throw new NumberFormatException();
		}
		//update inactive cost
		if(!texCostI.getText().isEmpty())
		{
			double d = Double.parseDouble(texCostI.getText());
			if(d >= 0)
			{
			ProcList.getProc(curID).setInactiveCost(d);
			change = true;
			}
			else
				throw new NumberFormatException();
		}
		
		return change;
	}
		
	private void comboActUsed() {
		//clear fields
		lTimeTex.setText("");
		uTimeTex.setText("");
		pNumTex.setText("");
		pDenomTex.setText("");

		//display selected processor action time constraints and p.faulty
		if(combo.getSelectedItem() != null && comboAct.getSelectedItem() != null)
		{
			int id = ((Processor) combo.getSelectedItem()).getProcID();
			int idAct = ((Action) comboAct.getSelectedItem()).getActID();

			if(ProcList.getProc(id).getProcAction(idAct) != null )
			{				
				int[] times = ProcList.getProc(id).getProcAction(idAct).getDelay();
				lTimeTex.setText("" + times[0]);
				uTimeTex.setText("" + times[1]);

				int[] prob = ProcList.getProc(id).getProcAction(idAct).getFaulty();
				pNumTex.setText("" + prob[0]);
				pDenomTex.setText("" + prob[1]);
			}
		}
	}

	private void savActBtnPressed() {
		if(combo.getSelectedItem() != null && comboAct.getSelectedItem() != null)
		{
			int id = ((Processor) combo.getSelectedItem()).getProcID();
			int idAct = ((Action) comboAct.getSelectedItem()).getActID();

			//if processor action exists, update it
			if(isActEdit)
			{
				saveProcAction(id, idAct);
			}

			//if not, create new action
			else if(isActAdd)
			{
				if(ProcList.getProc(id) != null && ProcList.getProc(id).getProcAction(idAct) == null)
				{
					ProcList.getProc(id).addAction(new ProcAction(idAct));
					ActionList.getAction(idAct).addProc(id);
					saveProcAction(id, idAct);
				}
			}
			else
				System.out.println("no update");
		}
		else
			System.out.println("no update");

		
		//update combo
		//combo = addComboBox();
		//set fields uneditable
		lTimeTex.setBackground(uneditColour);
		uTimeTex.setBackground(uneditColour);
		pDenomTex.setBackground(uneditColour);
		pNumTex.setBackground(uneditColour);
		
		lTimeTex.setEditable(false);
		uTimeTex.setEditable(false);
		pNumTex.setEditable(false);
		pDenomTex.setEditable(false);
		isActAdd = false;
		isActEdit = false;
	}

	private void clearActBtnPressed() {
		//remove selected processor action
		int id = ((Processor) combo.getSelectedItem()).getProcID();
		int idAct = ((Action) comboAct.getSelectedItem()).getActID();
		ProcList.getProc(id).removeAction(idAct);
		ActionList.getAction(idAct).removeProc(id);
		
	}

	private void editActBtnPressed() {
		// set editable

		lTimeTex.setEditable(true);
		uTimeTex.setEditable(true);
		pNumTex.setEditable(true);
		pDenomTex.setEditable(true);
		
		lTimeTex.setBackground(Color.white);
		uTimeTex.setBackground(Color.white);
		pDenomTex.setBackground(Color.white);
		pNumTex.setBackground(Color.white);

		isActEdit = true;

	}

	private void addBtnPressed()
	{
		if(combo.getSelectedItem() != null && comboAct.getSelectedItem() != null)
		{
			int id = ((Processor) combo.getSelectedItem()).getProcID();
			int idAct = ((Action) comboAct.getSelectedItem()).getActID();
			if(ProcList.getProc(id) != null && ProcList.getProc(id).getProcAction(idAct) == null)
			{
				//clear fields and set editable
				lTimeTex.setText("");
				uTimeTex.setText("");
				pNumTex.setText("");
				pDenomTex.setText("");

				lTimeTex.setEditable(true);
				uTimeTex.setEditable(true);
				pNumTex.setEditable(true);
				pDenomTex.setEditable(true);

				lTimeTex.setBackground(Color.white);
				uTimeTex.setBackground(Color.white);
				pDenomTex.setBackground(Color.white);
				pNumTex.setBackground(Color.white);

				isActAdd = true;
			}
			else
				LogTab.logInput("Action: " + id + " is already assigned to Processor: " + idAct );
		}
		}

	@Override
	protected void newBtnPressed() {
		//clear fields and set editable
		tex.setText("");
		texA.setText("");
		texCostA.setText("");
		texCostI.setText("");
		tex.setEditable(true);
		texA.setEditable(true);
		texCostA.setEditable(true);
		texCostI.setEditable(true);
		
		tex.setBackground(Color.white);
		texA.setBackground(Color.white);
		texCostA.setBackground(Color.white);
		texCostI.setBackground(Color.white);

		isNew = true;
	}
	
	@Override
	protected void editBtnPressed() {
		// set fields editable
		tex.setEditable(true);
		texA.setEditable(true);
		texCostA.setEditable(true);
		texCostI.setEditable(true);
		
		tex.setBackground(Color.white);
		texA.setBackground(Color.white);
		texCostA.setBackground(Color.white);
		texCostI.setBackground(Color.white);

		isEdit = true;		
	}

	@Override
	protected void remBtnPressed() {
		//remove selected processor
		if(combo.getSelectedItem() != null)
		{
			int id = ((Processor) combo.getSelectedItem()).getProcID();
			for(Object o : ProcList.getProc(id).getProcActions())
			{
				System.out.println(ActionList.getAction(((ProcAction) o).getAction()).getActName());
				ActionList.getAction(((ProcAction) o).getAction()).removeProc(id);
			}
			ProcList.removeProc(id);

			combo.removeItem(combo.getSelectedItem());
			combo.validate();

			ProcessorPanel.updateProcs();
		}
	}

	@Override
	protected void savBtnPressed()
	{
		boolean change = false;
		int curID;

		//if processor exists, update it
		if(isEdit){
			if(combo.getSelectedItem() != null)
			{
				curID = ((Processor) combo.getSelectedItem()).getProcID();

				int i = combo.getSelectedIndex();
				//update name if not already used
				if(tex.getText() != null && ProcList.getProc(tex.getText()) == null)
				{
					ProcList.getProc(curID).setProcName(tex.getText());
				}
				else
					LogTab.logInput("Processor name not changed");
				try
				{
					change = updateProc(curID, change);
				}
				catch(NumberFormatException n)
				{
					LogTab.logInput("Incorrectly formatted cost");
				}
				
				combo.setSelectedIndex(i);
				comboUsed();
			}
			else
				System.out.println("no update");
		}

		//if not, create new processor
		else if(isNew  && ProcList.getProc(tex.getText()) == null)
		{
			ProcList.addProc(tex.getText()); //set name
			curID = ProcList.getProc(tex.getText()).getProcID();
			change = updateProc(curID, change);
			
			combo.addItem(ProcList.getProc(curID));
			
			combo.setSelectedItem(ProcList.getProc(tex.getText()));
			 comboUsed();
		}
		else
		{
			System.out.println("no update");
		}

		tex.setBackground(uneditColour);
		texA.setBackground(uneditColour);
		texCostA.setBackground(uneditColour);
		texCostI.setBackground(uneditColour);
		
		tex.setEditable(false);
		texA.setEditable(false);
		texCostA.setEditable(false);
		texCostI.setEditable(false);
		isNew = false;
		isEdit = false;
		
		ProcessorPanel.updateProcs();

	}

	@Override
	protected void comboUsed() {
		//clear fields
		tex.setText("");
		texA.setText("");
		texCostA.setText("");
		texCostI.setText("");
		actDisp.setText("");
		lTimeTex.setText("");
		uTimeTex.setText("");
		pNumTex.setText("");
		pDenomTex.setText("");

		//display selected processor name, desc, and costs
		if(combo.getSelectedItem() != null)
		{
			tex.setText(((Processor) combo.getSelectedItem()).getProcName());
			texA.setText(((Processor) combo.getSelectedItem()).getProcDesc());
			texCostA.setText("" + ((Processor) combo.getSelectedItem()).getActiveCost());
			texCostI.setText("" + ((Processor) combo.getSelectedItem()).getInactiveCost());

			int curID = ((Processor) combo.getSelectedItem()).getProcID();
			//iterate over set of associated processor actions and display
			Iterator it = ProcList.getProc(curID).getProcActions().iterator();
			StringBuilder sb = new StringBuilder("");

			while (it.hasNext())
			{
				Object curr = it.next();			
				int id = ((ProcAction) curr).getAction();
				//if(ActionList.getAction(id) != null)
					sb.append(" " + ActionList.getAction(id).getActName() + "\n");
			}
			actDisp.setText(sb.toString());
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == newBtn)
		{
			newBtnPressed();
		}
		if(e.getSource() == editBtn)
		{
			editBtnPressed();
		}
		if(e.getSource() == remBtn)
		{
			remBtnPressed();			
		}
		if(e.getSource() == savBtn)
		{
			savBtnPressed();
		}
		if(e.getSource() == combo)
		{
			comboUsed();
		}
		if(e.getSource() == addActBtn)
		{
			addBtnPressed();
		}
		if(e.getSource() == editActBtn)
		{
			editActBtnPressed();
		}
		if(e.getSource() == clearActBtn)
		{
			clearActBtnPressed();
		}
		if(e.getSource() == savActBtn)
		{
			savActBtnPressed();
		}
		
		if(e.getSource() == comboAct)
		{
			comboActUsed();
		}
	}

	@Override
	public void stateChanged(ChangeEvent ce) {
		if(source.getSelectedComponent().equals(this))
		{
			refreshComboBox();
			refreshComboBoxAct();
		}
		
	}

	@Override
	protected void refreshComboBox() {
		combo.removeAllItems();
		for(Object o : ProcList.getList())
			combo.addItem((Processor) o);

		combo.validate();
		if(combo.getItemAt(0) != null)
		{
			combo.setSelectedIndex(0);
			comboUsed();
		}
	}
	
	private void refreshComboBoxAct() {
		comboAct.removeAllItems();
		for(Object o : ActionList.getList())
			comboAct.addItem((Action) o);

		comboAct.removeItemAt(0);
		comboAct.validate();
		if(combo.getItemAt(0) != null)
		{
			combo.setSelectedIndex(1);
			comboUsed();
		}
	}

}
